package com.summer;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class ZyxApplicationContext {
    private Class configClass;
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    private List<BeanPostProcessor> postProcessors = new ArrayList<>();

    public ZyxApplicationContext(Class configClass) {
        this.configClass = configClass;

        // 解析配置类
        // ComponentScan注解 --》 扫描路径 --》 扫描
        scan(configClass);

        // 实例化单例 bean
        createSingletonBean();
    }


    public Object getBean(String beanName) {
        if (beanDefinitionMap.containsKey(beanName)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals(ScopeEnum.singleton)) {
                return singletonObjects.get(beanName);
            } else {
                return createBean(beanName, beanDefinition);
            }
        } else {
            // 不存在bean
            throw new NullPointerException();
        }
    }

    /**
     * 扫描class，转化为BeanDefinition对象，最后添加到beanDefinitionMap中
     */
    private void scan(Class configClass) {
        // 先得到包路径
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScanAnnotation.value();
        path = path.replace(".", "/");
        Class<ZyxApplicationContext> zyxApplicationContextClass = ZyxApplicationContext.class;
        ClassLoader classLoader = zyxApplicationContextClass.getClassLoader();
        URL resource = classLoader.getResource(path);
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                String fileName = f.getAbsolutePath();
                if (fileName.endsWith(".class")) {
                    String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                    className = className.replace("\\", ".");
                    try {
                        Class<?> clazz = classLoader.loadClass(className);
                        if (clazz.isAnnotationPresent(Component.class)) {
                            // 表示当前类是一个Bean
                            // 解析类

                            // bean post processor , 将其加入 list
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                                postProcessors.add(beanPostProcessor);
                            }


                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();
                            if (beanName.equals("")) {
                                String[] names = className.split("\\.");
                                if (names.length != 0) {
                                    beanName = names[names.length - 1];
                                    char first = beanName.charAt(0);
                                    beanName = Character.toLowerCase(first) + beanName.substring(1);
                                }
                            }

                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setBeanClass(clazz);
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                String scopeValue = scopeAnnotation.value();
                                if (ScopeEnum.singleton.name().equals(scopeValue)) {
                                    beanDefinition.setScope(ScopeEnum.singleton);
                                } else {
                                    beanDefinition.setScope(ScopeEnum.prototype);
                                }
                            } else {
                                beanDefinition.setScope(ScopeEnum.singleton);
                            }
                            beanDefinitionMap.put(beanName, beanDefinition);
                        }
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException(e);
                    } catch (InstantiationException e) {
                        throw new RuntimeException(e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }

    }

    private void createSingletonBean() {
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals(ScopeEnum.singleton)) {
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }
    }

    /**
     * 基于BeanDefinition来创建bean
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getBeanClass();
        try {
            // 实例化
            Constructor declaredConstructor = clazz.getDeclaredConstructor();
            Object instance = declaredConstructor.newInstance();

            // 填充属性
            //利用class的反射，获取class对象中的全部属性
            for (Field field : clazz.getDeclaredFields()) {
                //在获取的属性中去判断是不是有autowired的注解，有的话，就会执行属性赋值的操作，这也就是依赖注入
                if (field.isAnnotationPresent(Autowired.class)) {
                    String name = field.getName();
                    Object bean = getBean(name);
                    field.setAccessible(true);
                    field.set(instance, bean);
                }
            }

            // Aware回调
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }

            // 初始化前
            for (BeanPostProcessor beanPostProcessor : postProcessors) {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            // 初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }

            // 初始化后
            for (BeanPostProcessor beanPostProcessor : postProcessors) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            }


            return instance;
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

    }


}